package ludichat.cobbreeding.mixin;

import com.cobblemon.mod.common.api.pokemon.PokemonProperties;
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.Pokemon;
import com.cobblemon.mod.common.pokemon.Species;
import ludichat.cobbreeding.*;
import ludichat.cobbreeding.components.CobbreedingComponents;
import net.minecraft.core.HolderLookup;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.WorldlyContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import org.jetbrains.annotations.NotNull;
import net.minecraft.sounds.SoundSource;
import net.minecraft.sounds.SoundEvents;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.util.List;

import static ludichat.cobbreeding.BreedingUtilities.chooseEgg;

@Mixin(PokemonPastureBlockEntity.class)
public abstract class PokemonPastureBlockEntityMixin implements PastureInventory, WorldlyContainer {
    @Unique
    private static final Logger LOGGER = Cobbreeding.LOGGER;
    @Unique
    private static final BooleanProperty HAS_EGG = CustomProperties.HAS_EGG;

    @Override
    public NonNullList<ItemStack> getItems() {
        int hash = this.hashCode();
        PastureBreedingData data;

        if (PastureBreedingData.registry.containsKey(hash)) {
            data = PastureBreedingData.registry.get(hash);
        } else {
            data = new PastureBreedingData(getConfig().getEggCheckTicks(), NonNullList.withSize(1, ItemStack.EMPTY));
            PastureBreedingData.registry.put(hash, data);
        }

        return data.getEgg();
    }

    /**
     * Get mod configuration
     *
     * @return Configuration instance
     */
    @Unique
    private static Config getConfig() {
        Config config;
        try{
            config = Cobbreeding.INSTANCE.getConfig();
        } catch (kotlin.UninitializedPropertyAccessException e) {
            LOGGER.warn("Trying to read configurations but they haven't been initialized. Returning Default configs instead. This might happen because the client is running logic it shouldn't.");
            return new Config();
        }
        return config;
    }

    @Inject(at = @At("HEAD"), method = "TICKER$lambda$14")
    private static void init(Level world, BlockPos pos, BlockState state, PokemonPastureBlockEntity blockEntity, CallbackInfo ci) {
        if (world.isClientSide) return;

        int hash = blockEntity.hashCode();
        PastureBreedingData data;

        if (PastureBreedingData.registry.containsKey(hash)) {
            data = PastureBreedingData.registry.get(hash);
        } else {
            data = new PastureBreedingData(getConfig().getEggCheckTicks(), NonNullList.withSize(1, ItemStack.EMPTY));
            PastureBreedingData.registry.put(hash, data);
        }

        world.setBlockAndUpdate(pos, state.setValue(HAS_EGG, !data.getEgg().getFirst().isEmpty()));

        int time = data.getTime();

        time--;
        if (time <= 0) {
            time = getConfig().getEggCheckTicks();
            List<PokemonPastureBlockEntity.Tethering> tetheredPokemon = blockEntity.getTetheredPokemon();
            List<Pokemon> pokemon = BreedingUtilities.getPokemon(tetheredPokemon);
            // Applying Mirror Herb effect to Pokemon holding it
            BreedingUtilities.applyMirrorHerb(pokemon);

            double randomNumber = Math.random();
            double eggChance = getConfig().getEggCheckChance();

            LOGGER.trace("Trying egg, roll: %b (%f >= 1 - %f)".formatted(randomNumber >= 1 - getConfig().getEggCheckChance(), randomNumber, eggChance));

            if (data.getEgg().getFirst().isEmpty() && randomNumber >= 1 - eggChance) {
                PokemonProperties eggData = chooseEgg(pokemon);
                ItemStack eggItem = null;

                if (eggData != null)
                {
                    // Creating the egg
                    if(eggData.getSpecies() != null)
                    {
                        Species egg_pokemon = PokemonSpecies.INSTANCE.getByName(eggData.getSpecies());
                        if (egg_pokemon != null)
                        {
                            // Choosing egg texture
                            FormData form = egg_pokemon.getStandardForm();
                            if (eggData.getForm() != null) {
                                form = egg_pokemon.getFormByShowdownId(eggData.getForm());
                            }

                            // Choosing egg color
                            if(Cobbreeding.config.getCustomColors())
                            {
                                eggItem = EggUtilities.selectEggItem(form);
                            }
                            else
                            {
                                eggItem = new ItemStack(Cobbreeding.EGG_ITEMS.get("pokemon_egg").get());
                            }

                            // 设置蛋品质外观
                            EggUtilities.setEggQuality(eggItem, eggData);

                            // Setting the egg timer
                            eggItem.set(
                                    CobbreedingComponents.TIMER.get(),
                                    EggUtilities.calculateTimer(egg_pokemon)
                            );
                        }
                        if (eggData.getSpecies().equals("random"))
                        {
                            eggItem = new ItemStack(Cobbreeding.EGG_ITEMS.get("pokemon_egg").get());
                            // Setting the egg timer
                            eggItem.set(
                                    CobbreedingComponents.TIMER.get(),
                                    EggUtilities.calculateTimer(PokemonSpecies.INSTANCE.random())
                            );
                        }
                        if (egg_pokemon != null || eggData.getSpecies().equals("random"))
                        {
                            eggItem.set(CobbreedingComponents.EGG_INFO.get(), eggData.asString(" ") + " aspects=" + eggData.getAspects().toString().replace(" ", ""));
                            // Adding the egg to the inventory
                            data.getEgg().set(0, eggItem);
                            // Playing a sound
                            world.playSound(null, pos, SoundEvents.CHICKEN_EGG, SoundSource.BLOCKS, 1f, 1f);
                        }
                    }

                    if (eggItem == null)
                        Cobbreeding.LOGGER.error("Couldn't add egg to pasture. Properties : " + eggData);
                }
            }
        }

        data.setTime(time);
    }

    @Inject(at = @At("HEAD"), method = "saveAdditional")
    private void writeNbt(CompoundTag nbt, HolderLookup.Provider provider, CallbackInfo ci) {
        ContainerHelper.saveAllItems(nbt, getItems(), provider);
    }

    @Inject(at = @At("TAIL"), method = "loadAdditional")
    private void readNbt(CompoundTag nbt, HolderLookup.Provider provider, CallbackInfo ci) {
//        LOGGER.info("Loading pasture inventory:");
//        for (var item : getItems()) { LOGGER.info("\t{}", item); }
        ContainerHelper.loadAllItems(nbt, getItems(), provider);
    }

    @Override
    public boolean canPlaceItemThroughFace(int slot, ItemStack stack, Direction direction) {
        return false;
    }

    @Override
    public boolean canTakeItemThroughFace(int slot, ItemStack stack, Direction direction) {
        return getConfig().getAllowHoppersToPullFromPastureBlock();
    }

    @Override
    public int @NotNull [] getSlotsForFace(Direction direction) {
        return new int[] {0};
    }
}
